
import pygame
import itertools
import uuid
import sys
import cProfile

pygame.init()

globalPRIO = 3 # 1-10 10 is important only

def pprint(priority, *strings):
    if priority >= globalPRIO:
        #print strings
        output = "P"+str(priority)+": "
        for string in strings:
            output += str(string)+" "
        print output
class System(object):
    """baseclass for systems"""
    world = None
    def __init__(self, world):
        self.world = world
        world.systems.append(self)

    def tick(self, dt):
        """"executed every tick, dt is the time passed since last tick"""
       # print dt

class Mover(System):
    """it moves"""
    #def __init__(self, arg):
    #    super(Mover, self).__init__()
    #    self.arg = arg
        
class Empty(object):
    """docstring for Empty"""
 


        

class MovingCmp:
 x = 10
 y = 10
 speed = 10
 direction = 300


class RenderCmp:
 size = 10
 color = 0x121212

class CollisionCmp:
 size = 10
 form = 'circle'

class StatCmp:
 collisions = 0
 life = 100


class EntityMeta(type):

    def __new__(cls, name, bases ,dct):
        if '__slots__' in dct:
            raise TypeError('__slots__ may not be defined in Entity subclasses')
        dct['__slots__'] = ('world', 'id')
        return type.__new__(cls, name, bases, dct)

class Entity(object):

    __metaclass__ = EntityMeta
    def __getattr__(self, name):
        """"""
        pprint (2, "entity getattr name :", name,"XX hasattr:",hasattr(self.world.components, name))
        #print self.world.components.index(name)
        component = getattr(self.world.components, name)
        pprint(2,"component: ", component)
        if self in component:
            pprint(2,"self in component")
            return component[self]
        else:
            pprint(2,"self not in comp")
            return component.add(self)
        #return EntityComponentAccessor(component, self)
        """
    def __init__(self,world):
        ""init/new ?""
        self.id = uuid.uuid4()##itertools?
        self.world = world
        world.entities.append(self)

        """

    def __new__(cls, world, *args, **kw):
        """new works prior to the changes to set and get; or NOT"""
        
        entity = object.__new__(cls)
        entity.world = world
        entity.id = world.newid()#uuid.uuid4()
        world.entities.addd(entity)
        pprint(2, "entity new world:",entity.world)
        return entity

#    def __init__(self, world):
#        """docst"""
#        self.__slots__ = ["world","id"]

    def __setattr__(self, name , value):
        """"""
        pprint(1,"entity setattr name:",name,"value:",value)
        if name == "world" or name =="id" or name=="__slots__":
            super(Entity, self).__setattr__(name, value)
            pprint(2,"entity setattr name:", name," value: ", value)
        else:
            component = getattr(self.world.components, name)
            pprint(2,"setattr else", component)

    def delete(self):
        """delete the ent from the world"""
        self.world.entities.remove(self)
    

#    def __repr__(self):
#        return "z"

    def __hash__(self):
        return self.id
    def __eq__(self, other):
        return self.id == other.id

class Component(dict):
    """Components store alle the data"""
    def __init__(self, world, **fields):
        pprint(1, "init comp")
        self.fields = {}
        #print fields
        for fname,fdef in fields.items():
            self.fields[fname]= fdef
            #print "component: " + fname + " " + fdef


        #setattr(world.components)= self

    def add(self, entity):
        data = self[entity] = Data(entity, self.fields)
        pprint(2,"cmp add")
        return data
"""
    def __setattr__(self, entity, name):
        if name in self: # ???
            self.entity = 6

    def __getattr__(self, entity):
        print "component getattr + "+ entity
        data = super(Component, self).__getattr__(self,entity)
        return data
"""



class Data(object):
    """dict for the component data"""
    def __init__(self,entity,fields,**data):
        self.__dict__['fields']=fields
        self.__dict__['entity']=entity
        for field in fields.keys():
            if field in data:
                setattr(self, field, data[field])
            else:
                setattr(self, field, fields[field])



#class EntitySystem:
#
# def getID(self):
#  return uuid.uuid4()


class Universe(list):
    """the universe hold all the worlds
    

    """
    worlds= []

class World(object):
    """the worlds hold their respective entities, components and systems"""
    components = None

    systems = []

    entities = None
    
    active = True

    def __init__(self , universe):
        """docstring for __init__"""

        self.entities = EntityContainer(self)
        self.components = Parts(self)
        self.newid = itertools.count().next
        ##register at universe
        universe.worlds.append(self)

##    def __getitem__(self, name):
##        if name == "...":
##           return self.entities


class EntityComponentAccessor(object):
    """helperclass to acces component data for a aspecific entity
    """
    __data = None
    def __init__(self, component, entity):
        clsname = self.__class__.__name__
        self.__dict__['_%s__component' % clsname] = component
        self.__dict__['_%s__entity' % clsname] = entity

    def __getattr__(self, name):
        pprint(2, "ECAgetattr name:", name)
        pprint(2, "ECA get comonent", self.__component,"ent:", self.__entity, "com[ent]")#, self.__component[self.__entity])
        return getattr(self.__component[self.__entity], name)

    def __setattr__(self, name, value):
        pprint(2, "ECA setattr name:", name, "value:" , value)
        if self.__entity in self.__component:
            pprint(2,"get data from comp")

        else:
            self.__component.add(self.__entity)
            pprint(2, "set new data")

class Parts(object):
    """holds world parts"""

    _world = None
    _parts = None
    def __init__(self, world):
        
        self._world = world
        self._parts = []
    def __setattr__(self, name, part):
        #print "parts setattr name: "+name+" part: "+part
        pprint(2, "parts setattr name:",name,"part:",part)
        if hasattr(self.__class__, name):
            if name.startswith("_"):
                super(Parts, self).__setattr__(name, part)
            else:
                pprint(10,"cant write",name,"in",self)
        else:
            self._parts.append(part)
            super(Parts,self).__setattr__(name, part)
        #else

    def remove(self, entity):
        pprint(2,"remove part", entity)
        if entity in self._parts:
            pprint(2,"remove part", entity)

    def __iter__(self):
        """docstring for __iter__"""
        return iter(tuple(self._parts))

class EntityContainer(object):
    """holds all entities and sorts entities by class"""
    _world = None
    _entities = None

    def __init__(self, world):
        self._world = world
        self._entities = {}

    def addd(self, entity):
        cls = entity.__class__
        if cls in self._entities:
            #pprint(5,"entcont:",cls)
            self._entities[cls].add(entity)
        else:
            self._entities[cls]= set([entity]) # sets not needed now()? bad perf
            #pprint(5,"EntCont add; _ent",self._entities)
            pprint(2,"EntCont add; cls:",cls,"")
            #for item in self_entities
    
    
    #TODO query by class
    #TODO 
    def __iter__(self):
        out = set([])
        for sets in self._entities.values():
            out = out | sets
        return iter(out)
        #return out

    def remove(self, entity):
        cls = entity.__class__
        #remove from EC set and delete dict entry if set is empty
        self._entities[cls].discard(entity)
        if len(self._entities[cls]) ==0:
            del self._entities[cls]
        
        #dele component data
        for component in self._world.components:
            try:
                del component[entity]
            except KeyError:
                pass
        #remove entity object
        del entity

    def __len__(self):
        """"""
        
        out = set([])
        for sets in self._entities.values():
            out = out | sets
        return len(out)
def testworld(world,verbose):
    """test if the RS is working correctly
    
    """
    v = verbose
    
    def listparts(worldpart):
        """list parts"""
        parts = ""
        partn = 0
        for part in worldpart:
            parts = parts + str(part.__class__.__name__)+" - "
            partn += 1

        return partn, parts
    
    pprint(v+1, "####### TESTING",world,"#######")
    pprint(v,"#############################")
    pprint(v+1,"running?",world.active)

    
    sysn, systems = listparts(world.systems)
    pprint(v+1, "#of systems:",sysn,"| ",systems)

    
    cmpn, components = listparts(world.components)
    pprint(v+1,"#of components:",cmpn,"| ",components)


    entn, entities = listparts(world.entities)
    pprint(v+1,"#of entities:",entn,"| ",entities)

    out = "ent id + world: "
    for entity in world.entities:
        out = out + str(entity.id)+"-"+str(entity.world)+" | "
    pprint(v+1, out)

    out = ""
    for entity in world.entities:
        out = "Entity(ID): "+ str(entity.id)+ " has the following components: "
        for component in world.components:
            if entity in component:
                out = out+ str(component.__class__.__name__)+ " | "
                for field in component.fields.keys():
                    out = out + field + ":"+str(getattr(component[entity],field))+" , "
        pprint(3,out)
        




# running
#
# #systems systems
# #cmp cmp
# #ent ent
#
# ent id -  ent world = world
# ent components ent data
# 
# cmp name - cmpworld = world
# cmp ent - cmp fields+defaults
# 
# system runtime
#
# ent change data
# 
# cpy ent
# 
# ent change world
#
#
#
#
#
#

def main():

    try:
        universe = Universe()
        world1 = World(universe)
        universe.append("6")
        clock = pygame.time.Clock()
        
        world1.components.comp1 = Component(world1, x1="float", x2="float" )

        system1 = System(world1)
        system2 = Mover(world1)
        entity1 = Entity(world1)

        
        pprint(2, world1.entities)
        
        pprint(2, "GO")
        entity1.comp1.x1 = 5
        pprint(2, "XXXX")
        pprint(3, entity1.comp1.x1)
        pprint(3, entity1.comp1.x2)

        testworld(world1,globalPRIO)
        entity1.delete()

        testworld(world1,globalPRIO)
        # for x in itertools.count(1):
            #print x


        for i in range(7005):
            for t in range(1000):
                x = Entity(world1)
                x.comp1.x1 = 10
                Empty()
                x.delete()
           # x = resource.getrusage(resource.RUSAGE_SELF)
            pprint(8,"#ent/",i,":",len(world1.entities))#,"MB:",x)



        #testworld(world1,-1)
        while False:
            for event in pygame.event.get():
                if event.type==QUIT():
                    exit()
            
            dt = clock.tick()
            for worlds in universe.worlds:
                for system in worlds.systems:
                    system.tick(dt)
                                
            #Entity(world1)

    except KeyboardInterrupt:
        print "User interrupt...exiting"
        print len(world1.entities)
    except Exception, e:
        print "Unexpected Error: %s" %str(e)
    exit()


if __name__ == "__main__":
    cProfile.run('main()')

def exit():
    pygame.quit()
    sys.exit()


